﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;

namespace Redbrick.Silverlight.Common
{
	public static partial class Enums
	{
		public static int Int<_type>(this _type e) { return Convert.ToInt32(e); }

		/// <summary>
		/// get a [Description] attribute for the given value
		/// </summary>
		public static string Description<_type>(this _type value)
		{
			Type type = value.GetType();
			FieldInfo fieldInfo = type.GetField(value.ToString());
			DescriptionAttribute[] descriptionAttributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);

			if (descriptionAttributes != null && descriptionAttributes.Length > 0)
				return descriptionAttributes[0].Description;
			return fieldInfo.GetValue(value).ToString();
		}

		/// <summary>
		/// get the description for eatch item in the list
		/// </summary>
		public static List<string> Descriptions<_type>(this IEnumerable<_type> items)
		{
			List<string> str = new List<string>();
			items.ForEach(itm => str.Add(itm.Description()));
			return str;
		}

		/// <summary>
		/// test an enum for a flag
		/// </summary>
		public static bool Has<_type>(this Enum me, _type value)
		{

			if (!value.GetType().IsEnum) throw new InvalidOperationException("value is not an enum type");
			try
			{
				return HasBit(Convert.ToInt64(me), Convert.ToInt64(value));
			}
			catch
			{
				return false;
			}
		}

		/// <summary>
		/// compare the values of two enums
		/// </summary>
		public static bool Is<_type>(this Enum me, _type value)
		{
			try
			{
				if (!value.GetType().IsEnum) throw new InvalidOperationException("value is not an enum type");
				return (int)(object)me == (int)(object)value;
			}
			catch
			{
				return false;
			}
		}

		/// <summary>
		/// add a flag to an enum
		/// </summary>
		public static _type Add<_type>(this _type me, _type value)
		{
			if (!value.GetType().IsEnum) throw new ArgumentException(value + " is not an enumerated type");
			try
			{
				if (me.Equals(0) || me.Equals(value)) return value;

				long n = SetBit(Convert.ToInt64(me), Convert.ToInt64(value));
				return n.ToEnum<_type>();
			}
			catch (Exception ex)
			{
				throw new ArgumentException(
					 string.Format(
						  "Could not append value to enumerated type '{0}'.",
						  value.GetType().Name
						  ), ex);
			}
		}

		/// <summary>
		/// remove a flag from an enum
		/// </summary>
		public static _type Remove<_type>(this Enum type, _type value)
		{
			try
			{
				return RemoveBit(Convert.ToInt64(type), Convert.ToInt64(value)).ToEnum<_type>();
			}
			catch (Exception ex)
			{
				throw new ArgumentException(
					 string.Format(
						  "Could not remove value from enumerated me '{0}'.",
						  typeof(_type).Name
						  ), ex);
			}
		}

		/// <summary>
		/// get a list of the possible values of an enum
		/// </summary>
		public static List<_type> GetValues<_type>()
		{
			Type enumType = typeof(_type);

			if (!enumType.IsEnum)
			{
				throw new ArgumentException("Type '" + enumType.Name + "' is not an enum");
			}

			var fields = from field in enumType.GetFields()
							 where field.IsLiteral
							 select (_type)field.GetValue(enumType);
			return fields.ToList<_type>();
		}

		public static bool HasBit(this Enum type, int value)
		{
			try
			{
				return (((int)(object)type & value) == value);
			}
			catch
			{
				return false;
			}
		}
		public static bool HasBit(this Enum type, long value)
		{
			try
			{
				return (((long)(object)type & value) == value);
			}
			catch
			{
				return false;
			}
		}

		public static _type Parse<_type>(this string me)
		{
			if (typeof(_type).IsEnum)
			{
				object obj = Enum.Parse(typeof(_type), me, true);
				return (_type)obj;
			};
			throw new NotSupportedException(typeof(_type).Name + " must be an enum");
		}
		private static _type ToEnum<_type>(this long n)
		{
			return (_type)Enum.ToObject(typeof(_type), n);

		}

        public static long SetBit(this long toTest, long flags)
        {
            long res = toTest | flags;
            return res;
        }
        public static long RemoveBit(this long toTest, long flags)
        {
            long res = toTest & (~flags);
            return res;
        }
        public static bool HasBit(this long toTest, long mask)
        {
            return (toTest & mask) != 0;
        }
       
        public static int SetBit(this int n, int flags)
        {           
            return (int)((long)n).SetBit( (long)flags) ;
        }
        public static int RemoveBit(this int n, int flags)
        {
            return (int)((long)n).RemoveBit((long)flags);
        }
        public static bool HasBit(this int n, int mask)
        {
            return ((long)n).HasBit((long)mask);
        }

        public static uint SetBit(this uint n, uint flags)
        {
            return (uint)((long)n).SetBit((long)flags);
        }
        public static uint RemoveBit(this uint n, uint flags)
        {
            return (uint)((long)n).RemoveBit((long)flags);
        }
        public static bool HasBit(this uint n, uint mask)
        {
            return ((long)n).HasBit((long)mask);
        }

        public static uint SetBit(this uint n, int flags)
        {
            return (uint)((long)n).SetBit((long)flags);
        }
        public static uint RemoveBit(this uint n, int flags)
        {
            return (uint)((long)n).RemoveBit((long)flags);
        }
        public static bool HasBit(this uint n, int mask)
        {
            return ((long)n).HasBit((long)mask);
        }

        public static int SetBit(this int n, uint flags)
        {
            return (int)((long)n).SetBit((long)flags);
        }
        public static int RemoveBit(this int n, uint flags)
        {
            return (int)((long)n).RemoveBit((long)flags);
        }
        public static bool HasBit(this int n, uint mask)
        {
            return ((long)n).HasBit((long)mask);
        }
    }
}
